#region license
# This file is part of Grawlix.
# 
# Grawlix is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
# 
# Grawlix is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public
# License along with Grawlix.  If not, see
# <http://www.gnu.org/licenses/>.
#endregion

"""An object used to create texture resources."""

namespace Grawlix.Services.Rendering
import Grawlix
import Grawlix.Meta
import Grawlix.Services.Resources
import C5
import ConcurrentBoo
import System

final class DefaultTextureFactory(
	AbstractResourceFactory[of ITextureResource]):
"""Create and cache texture objects."""
	
	def constructor(
		[required] imageResolver as ITypedResolver[of Image],
		[required] renderDevice as IRenderDevice,
		[required] uriResolver as IUriResolver):
	"""
	Constructs a new texture factory.
	Param imageResolver: The image loader.
	Param renderDevice: The rendering engine device.
	Param uriResolver: Used to complete URIs.
	"""
		super(uriResolver)
		_imageResolver = imageResolver
		_renderDevice = renderDevice
	
	protected override def StartLoad(identifier as Uri):
	"""
	Loads or reloads the state for the given identifier.
	Param identifier: The URI of the resource to load.
	"""
		computation = _channel.Enqueue() do:
			image = _imageResolver.Load(identifier, false)
			return image
		_mappedTexture[computation] = identifier
		_activeComputation[identifier] = computation
	
	protected override def CancelLoad(identifier as Uri):
	"""
	Loads or reloads the state for the given identifier.
	Param identifier: The URI of the resource to load.
	"""
		_activeComputation.Remove(identifier)
	
	protected override def StartUnload(state):
	"""
	Unloads the given resource.
	Param state: The resource state information, which may be null.
	"""
		_unloadQueue.Add(state)
	
	protected override def UpdateState(elapsed as timespan):
	"""
	Updates the factory.
	Param elapsed: The amount of time since the last frame.
	"""
		for state in _unloadQueue:
			_renderDevice.UnloadTexture(state)
		_unloadQueue.Clear()
		computation as Computation
		while _channel.TryDequeue(computation):
			assert computation.Finished
			identifier = _mappedTexture[computation]
			_mappedTexture.Remove(computation)
			continue unless _activeComputation.Contains(identifier) and \
				_activeComputation[identifier] == computation
			_activeComputation.Remove(identifier)
			if computation.Failure is not null:
				log Warn, "Error occurred while loading " + \
					"a texture in DefaultTextureFactory.\n" + \
					"${computation.Failure}"
				SetState(identifier, null, true)
				continue
			result as Image = computation.Value
			assert result is not null
			state = _renderDevice.LoadTexture(result)
			assert state is not null
			SetState(identifier, state, false)
			log Debug, "Texutre ${identifier} loaded successfully."
	
	#[lock(Cache)]
	#def Reset():
	#"""Reload all textures but do not unload them first."""
	#	_channel = ComputationChannel()
	#	for weak in Cache.Values:
	#		resource as ITextureResource = weak.Target
	#		continue if resource is null
	#		resource.Load(null, false)
	#		LoadTexture(resource)
	
	override def ToString():
	"""
	Get a string representation for this object.
	Returns: A string representation for this object.
	"""
		return "DefaultTextureFactory"
	
	override Creator:
	"""The resource creator."""
		get:
			return _creator
	
	private _creator = DefaultTextureProxyCreator()
	"""The resource creator."""
	
	private _activeComputation = \
		HashDictionary[of Uri, Computation]()
	"""The currently active computation for a texture."""
	
	private _mappedTexture = \
		HashDictionary[of Computation, Uri]()
	"""The texture that a computation maps to."""
	
	private _unloadQueue = LinkedList[of object]()
	"""Queue of expired data, awaiting unload."""
	
	private _channel = ComputationChannel()
	"""A channel of concurrent calculations."""
	
	private _imageResolver as ITypedResolver[of Image]
	"""Resolve bitmap objects."""
	
	private _renderDevice as IRenderDevice
	"""Rendering device."""

#TODO: Replace explicit implementations with proxy ASTAttribute
internal final class DefaultTextureResourceProxy(
	AbstractResourceProxy[of ITextureResource],
	# FIXME: Workaround; shouldn't have to derive directly.
	ITextureResource):
"""The proxy used to access resources."""
	
	def constructor([required] identifier as Uri):
	"""
	Initializes a new proxy.
	Param identifier: The unique URI of the resource.
	"""
		super(identifier)
	
	Dimensions:
	"""The dimensions of the texture."""
		get:
			return State.Dimensions
	
	State as ITextureState:
	"""The internally used resource state."""
		get:
			return GetState()
	
	# FIXME: Ahh, the workarounds...
	override Ready:
	"""Whether or not the resource is ready."""
		get:
			return super.Ready
	
	override LoadFailed:
	"""Whether or not the load has failed."""
		get:
			return super.LoadFailed

	override Identifier:
	"""The unique identfier for this resource."""
		get:
			return super.Identifier

internal final class DefaultTextureProxyCreator(
	AbstractResourceProxyCreator[of ITextureResource]):
"""Creator for texture resources."""
	
	override def Create([required] identifier as Uri):
	"""
	Creates a new proxy.
	Param identifier: The identifier of the proxy.
	Returns: The new proxy.
	"""
		return DefaultTextureResourceProxy(identifier)
